const { findUser } = require("./account");
const { userCollection, LOG_TYPE } = require("../../common/constants");
const { ERROR } = require("../../common/error");
const { logout } = require("./logout");
const PasswordUtils = require("./password");

async function realPreLogin(params = {}) {
  const { user } = params;
  const appId = this.getUniversalClientInfo().appId;
  const { total, userMatched } = await findUser({
    userQuery: user,
    authorizedApp: appId,
  });
  if (userMatched.length === 0) {
    if (total > 0) {
      throw {
        errCode: ERROR.ACCOUNT_NOT_EXISTS_IN_CURRENT_APP,
      };
    }
    throw {
      errCode: ERROR.ACCOUNT_NOT_EXISTS,
    };
  } else if (userMatched.length > 1) {
    throw {
      errCode: ERROR.ACCOUNT_CONFLICT,
    };
  }
  const userRecord = userMatched[0];
  checkLoginUserRecord(userRecord);
  return userRecord;
}

async function preLogin(params = {}) {
  const { user } = params;
  try {
    const user = await realPreLogin.call(this, params);
    return user;
  } catch (error) {
    await this.middleware.uniIdLog({
      success: false,
      data: user,
      type: LOG_TYPE.LOGIN,
    });
    throw error;
  }
}

async function preLoginWithPassword(params = {}) {
  const { user, password } = params;
  try {
    const userRecord = await realPreLogin.call(this, params);
    const { passwordErrorLimit, passwordErrorRetryTime } = this.config;
    const { clientIP } = this.getUniversalClientInfo();
    // 根据ip地址，密码错误次数过多，锁定登录
    let loginIPLimit = userRecord.login_ip_limit || [];
    // 清理无用记录
    loginIPLimit = loginIPLimit.filter(
      (item) =>
        item.last_error_time > Date.now() - passwordErrorRetryTime * 1000,
    );
    let currentIPLimit = loginIPLimit.find((item) => item.ip === clientIP);
    if (currentIPLimit && currentIPLimit.error_times >= passwordErrorLimit) {
      throw {
        errCode: ERROR.PASSWORD_ERROR_EXCEED_LIMIT,
      };
    }
    const passwordUtils = new PasswordUtils({
      userRecord,
      clientInfo: this.getUniversalClientInfo(),
      passwordSecret: this.config.passwordSecret,
    });

    const { success: checkPasswordSuccess, refreshPasswordInfo } =
      passwordUtils.checkUserPassword({
        password,
      });
    if (!checkPasswordSuccess) {
      // 更新用户ip对应的密码错误记录
      if (!currentIPLimit) {
        currentIPLimit = {
          ip: clientIP,
          error_times: 1,
          last_error_time: Date.now(),
        };
        loginIPLimit.push(currentIPLimit);
      } else {
        currentIPLimit.error_times++;
        currentIPLimit.last_error_time = Date.now();
      }
      await userCollection.doc(userRecord._id).update({
        login_ip_limit: loginIPLimit,
      });
      throw {
        errCode: ERROR.PASSWORD_ERROR,
      };
    }
    const extraData = {};
    if (refreshPasswordInfo) {
      extraData.password = refreshPasswordInfo.passwordHash;
      extraData.password_secret_version = refreshPasswordInfo.version;
    }

    const currentIPLimitIndex = loginIPLimit.indexOf(currentIPLimit);
    if (currentIPLimitIndex > -1) {
      loginIPLimit.splice(currentIPLimitIndex, 1);
    }
    extraData.login_ip_limit = loginIPLimit;
    return {
      user: userRecord,
      extraData,
    };
  } catch (error) {
    await this.middleware.uniIdLog({
      success: false,
      data: user,
      type: LOG_TYPE.LOGIN,
    });
    throw error;
  }
}

function checkLoginUserRecord(user) {
  switch (user.status) {
    case undefined:
    case 0:
      break;
    case 1:
      throw {
        errCode: ERROR.ACCOUNT_BANNED,
      };
    case 2:
      throw {
        errCode: ERROR.ACCOUNT_AUDITING,
      };
    case 3:
      throw {
        errCode: ERROR.ACCOUNT_AUDIT_FAILED,
      };
    case 4:
      throw {
        errCode: ERROR.ACCOUNT_CLOSED,
      };
    default:
      break;
  }
}

async function thirdPartyLogin(params = {}) {
  const { user } = params;
  return {
    mobileConfirmed: !!user.mobile_confirmed,
    emailConfirmed: !!user.email_confirmed,
  };
}

async function postLogin(params = {}) {
  const { user, extraData, isThirdParty = false } = params;
  const { clientIP } = this.getUniversalClientInfo();
  const uniIdToken = this.getUniversalUniIdToken();
  const uid = user._id;
  const updateData = {
    last_login_date: Date.now(),
    last_login_ip: clientIP,
    ...extraData,
  };
  const createTokenRes = await this.uniIdCommon.createToken({
    uid,
  });

  const { errCode, token, tokenExpired } = createTokenRes;
  if (errCode) {
    throw createTokenRes;
  }

  if (uniIdToken) {
    try {
      await logout.call(this);
    } catch (error) {}
  }

  await userCollection.doc(uid).update(updateData);
  await this.middleware.uniIdLog({
    data: {
      user_id: uid,
    },
    type: LOG_TYPE.LOGIN,
  });
  return {
    errCode: 0,
    newToken: {
      token,
      tokenExpired,
    },
    uid,
    ...(isThirdParty
      ? thirdPartyLogin({
          user,
        })
      : {}),
    passwordConfirmed: !!user.password,
  };
}

module.exports = {
  preLogin,
  postLogin,
  checkLoginUserRecord,
  preLoginWithPassword,
};
